package com.frinder.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.HashedWheelTimer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * Created by kiktech on 2017/8/4.
 */
@Slf4j
@Component
@ConfigurationProperties(prefix = "netty")
public class NettyClient {

    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    protected final HashedWheelTimer timer = new HashedWheelTimer();

    private Channel channel;

    @Value("${host:127.0.0.1}")
    private String host = "127.0.0.1";

    @Value("${port:8082}")
    private int port = 8082;


    /**
     * 启动客户端
     */
    public void start() {
        log.debug("Begin to connect netty Server！");
        try {
            Bootstrap bootstrap = new Bootstrap();
            final ConnectionWatchDog watchdog = new ConnectionWatchDog(bootstrap,
                    timer, port, host, true) {
                @Override
                public ChannelHandler[] handlers() {
                    return new ChannelHandler[]{
                            this,
                            new LoggingHandler(LogLevel.INFO),
                            new NettyClientInitializer.NettyClientMessageFilter(),
                            // encode & decode
                            MarshallingCodeCFactory.decoder(),
                            MarshallingCodeCFactory.encoder(),
                            // message handler
                            new NettyClientInitializer.NettyClientHandler()
                    };
                }
            };
            ChannelFuture future;
            synchronized (bootstrap) {
                bootstrap.group(workerGroup)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.SO_KEEPALIVE, true)
//                        .handler(new NettyClientInitializer());
                        .handler(new ChannelInitializer<Channel>() {
                            @Override
                            protected void initChannel(Channel channel) throws Exception {
                                channel.pipeline().addLast(watchdog.handlers());
                            }
                        });
                future = bootstrap.connect(host, port).sync();
                channel = future.channel();
                log.debug("Connect netty Server complete！");
            }
        } catch (Exception e) {
            log.error("Connect netty Server failure！", e.getMessage(), e);
            destroy();
        }
    }

    /**
     * shutdown
     */
    public void destroy() {
        workerGroup.shutdownGracefully();
    }

    /**
     * 发送消息
     *
     * @param request
     */
    public void send(NettyRequest request) {
        if (null == channel) {
            throw new NullPointerException("Channel is null!");
        }
        channel.writeAndFlush(request);
    }

    /**
     * 使用此方法测试，请为 host/port设置值
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        NettyClient client = new NettyClient();
        client.start();
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        NettyRequest request;
        String message;
        while (true) {
            message = in.readLine();
            if ("stop".equalsIgnoreCase(message)) {
                client.destroy();
                return;
            }
            request = new NettyRequest(message);
            client.send(request);
        }
    }

}
